const person = {
  name: "Tom",
  age: 20,
  address: {
    provice: "北京",
    city: "北京",
  },
};
{
  const proxy = new Proxy(person, {
    get(target, property, receiver) {
      console.log(property);
      return target[property];
    },

    set(target, prop, value, receiver) {
      console.log(prop, value);
      target[prop] = value;
      return true;
    },
  });

  console.log(proxy.name);
  proxy.age = 21;
  console.log(proxy.age);
}

// get
{
  let array = [];
  const arrayProxy = new Proxy(array, {
    get(target, property, receiver) {
      return Reflect.get(target, property, receiver) || 0;
    },
  });

  console.log(arrayProxy[2]);
}

// set

{
  let array = [];
  const arrayProxy = new Proxy(array, {
    set(target, property, value, receiver) {
      // 允许 length 属性自由更新
      if (property === "length") {
        target[property] = value;
        return true;
      }

      // 非数字值拒绝设置
      if (typeof value !== "number") {
        return false;
      }
      target[property] = value;

      return true;
    },
  });

  arrayProxy.push(1);
  arrayProxy.push(2);
  try {
    arrayProxy.push("1"); // 不再报错，但不会插入值
  } catch (error) {}
  console.log(arrayProxy); // 输出: [1, 2, empty] (length=3)
}

// ownkeys & getOwnPropertyDescriptor
{
  let user = {
    name: "John",
    age: 30,
    _password: "***",
  };

  user = new Proxy(user, {
    ownKeys(target) {
      // return Object.keys(target).filter((key) => !key.startsWith("_"));
      return Reflect.ownKeys(target).filter((key) => !key.startsWith("_"));
    },
  });

  for (const key in user) {
    console.log(key, user[key]);
  }

  // 若 ownKeys 返回原始数组中不存在的对象，for...in 不会遍历，
  // 遍历只会针对对象有 enumerable 标识的属性。为了检查它，会对每个属性调用内部方法[[GetOwnProperty]] 来获取属性描述符（descriptor）, 原始数组没有属性，描述符为空。 需要使用 getOwnPropertyDescriptor

  let user1 = {};
  user1 = new Proxy(user1, {
    ownKeys(target) {
      return ["name", "age", "_password"];
    },
  });

  console.log("name" in user1);

  for (const key in user1) {
    console.log(key, user1[key]);
  }

  console.log("------ 分割线 ------");
  user1 = new Proxy(user1, {
    ownKeys(target) {
      return ["name", "age", "_password"];
    },

    getOwnPropertyDescriptor(target, property) {
      return {
        enumerable: true,
        configurable: true,
        writable: true,
      };
    },
  });

  console.log("name" in user1);

  for (const key in user1) {
    console.log(key, user1[key]);
  }
}

// deleteProperty
{
  let user = {
    name: "John",
    _password: "123",
  };

  user = new Proxy(user, {
    deleteProperty(target, property) {
      if (property.startsWith("_")) {
        throw new Error("Access denied");
      }
      delete target[property];
      return true;
    },
  });

  console.log(user);
}

{
  let user = {
    name: "John",
  };

  function wrap(target) {
    return new Proxy(target, {
      get(target, property, receiver) {
        if (property in target) {
          return Reflect.get(...arguments);
        }
        throw new ReferenceError(`Property doesn't exist: "${property}"`);
      },
    });
  }

  user = wrap(user);

  console.log(user.name);
  try {
    console.log(user.age);
  } catch (error) {
    console.log(error);
  }
}

{
  let array = [1, 2, 3];
  array = new Proxy(array, {
    get(target, property, receiver) {
      if (property < 0) {
        property = +property + target.length;
      }
      return Reflect.get(target, property, receiver);
    },
  });

  console.log(array[-1]);
  console.log(array[-2]);
}

{
  function makeObservable(target) {
    let callBack = null;
    const proxy = new Proxy(target, {
      set(target, prop, value, receiver) {
        callBack?.(prop, value);
        return Reflect.set(...arguments);
      },
    });

    proxy.observe = (fn) => {
      callBack = fn;
    };

    return proxy;
  }

  let user = {};
  user = makeObservable(user);

  user.observe((key, value) => {
    console.log(`SET ${key}=${value}`);
  });

  user.name = "John";
}
